﻿using DotNetCommon;
using DotNetCommon.Data;
using DotNetCommon.Extensions;
using MicroScheduleServer.Dtos;
using System.Collections.Concurrent;

namespace MicroScheduleServer.Services
{
    public class JobService
    {
        public static ConcurrentDictionary<string, Task> _tasks = [];
        public static HttpClient httpClient = new();

        public async Task<Result<string>> CreateJob(CreateJobReq req)
        {
            if (req == null) return Result.NotOk("参数为空!");

            var trigger = CreateTrigger(req);
            var job = CreateJob(req, trigger);

            if (job.Trigger == null)
            {
                //once
                await RunJob(job);
            }
            else
            {
                AddTaskLoop(job);
            }
            return Result.Ok(job.UUID);
        }

        private Trigger CreateTrigger(CreateJobReq req)
        {
            if (req.TriggerType == EnumTriggerType.Once) return null;
            else if (req.TriggerType == EnumTriggerType.Interval) return new Trigger
            {
                UUID = Guid.NewGuid().ToString("N"),
                Name = req.Name + " 的 trigger",
                Remark = null,
                TriggerType = EnumTriggerType.Interval,
                Interval = req.Interval >= 30 ? req.Interval : throw new Exception($"interval必须大于等于30s!"),
            };
            else return new Trigger
            {
                UUID = Guid.NewGuid().ToString("N"),
                Name = req.Name + " 的 trigger",
                Remark = null,
                TriggerType = EnumTriggerType.Repeat,
                LoopFrequency = req.LoopFrequency > 30 ? req.LoopFrequency : throw new Exception($"interval必须大于等于30s!"),
                LoopFilters = req.LoopFilters
            };
        }

        public Job CreateJob(CreateJobReq req, Trigger trigger)
        {
            return new Job
            {
                UUID = Guid.NewGuid().ToString("N"),
                Name = req.Name,
                Remark = req.Remark,
                Trigger = trigger,
                HttpHeaders = req.HttpHeaders,
                HttpMethod = req.HttpMethod,
                HttpUrl = req.HttpUrl,
                StartTime = req.StartTime ?? DateTime.Now,
            };
        }

        public async Task RunJob(Job job)
        {
            
        }

        public async Task RunJobWithFilter(Job job)
        {
            var dt = DateTime.Now;
            if (!IsFilterPass(job, dt))
            {
                return;
            }
            await RunJob(job);
        }

        private bool IsFilterPass(Job job, DateTime dt)
        {
            var fitlers = job.Trigger.LoopFilters.IfNullUseNew();
            var filterAct = (int actual, Trigger.LoopFilter filter) =>
            {
                List<int> values = null;
                if (filter.OpType == EnumOpType.BeTween)
                {
                    values = filter.Value.SplitAndTrimTo<int>(",");
                }
                else
                {
                    values = new List<int>(int.Parse(filter.Value));
                }
                var flag = true;
                switch (filter.OpType)
                {
                    case EnumOpType.Equal:
                        flag = actual == values[0];
                        break;
                    case EnumOpType.GreaterThan:
                        flag = actual > values[0];
                        break;
                    case EnumOpType.GreaterThanOrEqual:
                        flag = actual >= values[0];
                        break;
                    case EnumOpType.LessThan:
                        flag = actual < values[0];
                        break;
                    case EnumOpType.LessThanOrEqual:
                        flag = actual <= values[0];
                        break;
                    case EnumOpType.NotEqual:
                        flag = actual != values[0];
                        break;
                    case EnumOpType.BeTween:
                        flag = actual > values[0] && dt.Year < values[1];
                        break;
                    case EnumOpType.In:
                        flag = values.Contains(actual);
                        break;
                    default:
                        break;
                }
                if (!flag)
                {
                    return false;
                }
                return true;
            };
            foreach (var filter in fitlers)
            {
                int actual = -1;
                switch (filter.Property)
                {
                    case EnumProperty.Year:
                        actual = dt.Year;
                        break;
                    case EnumProperty.Month:
                        actual = dt.Month;
                        break;
                    case EnumProperty.Day:
                        actual = dt.Day;
                        break;
                    case EnumProperty.Hour:
                        actual = dt.Hour;
                        break;
                    case EnumProperty.Minute:
                        actual = dt.Minute;
                        break;
                    case EnumProperty.Second:
                        actual = dt.Second;
                        break;
                    case EnumProperty.DayOfWeek:
                        actual = (int)dt.DayOfWeek;
                        break;
                    case EnumProperty.DayOfYear:
                        actual = dt.DayOfYear;
                        break;
                    default:
                        break;
                }
                var flag = filterAct(actual, filter);
                if (!flag) return false;
            }
            return true;
        }

        public void AddTaskLoop(Job job)
        {
            _tasks.TryAdd(job.UUID, Task.Run(async () =>
            {
                if (job.StartTime < DateTime.Now)
                {
                    var off = DateTime.Now - job.StartTime;
                    await Task.Delay(off);
                }
                while (true)
                {
                    if (job.Trigger.Interval > 0)
                    {
                        await RunJob(job);
                        await Task.Delay(job.Trigger.Interval * 1000);
                        continue;
                    }
                    else
                    {
                        await RunJobWithFilter(job);
                        await Task.Delay(job.Trigger.LoopFrequency * 1000);
                        continue;
                    }
                };
            }));
        }
    }
}
